<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,height=device-height">
    <link rel="shortcut icon" href="/chart.ico" >
    <title>chart-{{log_dir}}</title>
    <style>::-webkit-scrollbar{display:none;}html,body{overflow:auto;height:100%;margin:0;}</style>
    <link href="/static/css/bootstrap.css" rel="stylesheet"/>
    <link href="/static/css/table.css" rel="stylesheet"/>
    <link href="/static/css/bootstrap-slider.css" rel="stylesheet"/>
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/bootstrap.js"></script>
    <script src="/static/js/bootbox.js"></script>
    <script src="/static/js/g2.min.js"></script>
    <script src="/static/js/bootstrap-slider.js"></script>
    <script src="/static/js/chart.js"></script>
    <style>
        .slider-selection {
	        background: #B8B8B8;
        }

    </style>
    <script>
        var prompt = function (message, time) {
            $("#alertDiv").delay(time || 5000).fadeOut();
            const id  = "alertDiv_" + new Date().getTime() + "_" + (Math.random() * 10000).toFixed();
            $("body").append(`
            <div id="${id}" class="alert alert-success" role="alert" style="position: fixed; top: 12px; left: 12px;
            z-index:999999">
                  ${message}
                </div>
            `);
            const $alertDiv = $("#" + id);
            $alertDiv.delay(time || 5000).fadeOut(() => {
                $alertDiv.remove();
            });
        };

        // 成功提示
        var success_prompt = function(message, time)
        {
            prompt(message, time);
        };
    </script>
<style>
    .g2-legend {
        width: 250px !important;
        height: 100%;
        font-size: 14px !important;
        color: #595959 !important;
    }

    .g2-legend-list {
        margin-top: 10px !important
    }

    .g2-legend-text {
        margin-top: 3px !important;
        color: rgb(140, 140, 140);
        display: inline-flex;
    }

</style>
</head>
<body>
    <div style="overflow:hidden;padding:5px 200px 0px 10px;">
        <button id="range" class="btn btn-info" style="margin-right: 5px;float:left;height:20px;padding: 0px 5px 0px 3px"
                data-toggle="modal" data-target="#range_box">
            <i class="glyphicon glyphicon-cog"></i> Range
        </button>
        <div style="visibility: hidden"
         id="progress-bar-div">
            <div style="float:left;margin-right:5px;margin-left: 5px">
                <span>Progress: </span>
            </div>
            <div class="progress progress-striped active" style="margin-bottom: 0px" id="progress">
                <div class="progress-bar progress-bar-success" role="progressbar"
                     aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
                     style="width: 0;" id="progress-bar">
                    <span id="progress-bar-text" style="color: black"></span>
                </div>
            </div>
        </div>
    </div>
    <h3 style="text-align:center;visibility: hidden;padding: 3px 0px 3px 0px" id="loss_title">loss trend in {{log_dir}}</h3>
    <div id="loss"></div>
    <h3 style="text-align:center;visibility: hidden;padding: 3px 0px 3px 0px" id="metric_title">metric trend in {{log_dir}}</h3>
    <div id="metric"></div>
    <script>/*Fixing iframe window.innerHeight 0 issue in Safari*/document.body.clientHeight;</script>

    <!--range-->
    <div class="modal fade" id="range_box" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"
            style="font-size: 18px">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            <h4 class="modal-title">Enable to choose range</h4>
          </div>
          <div class="modal-body" id="range_modal">

          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal" id="cancel_range">Cancel</button>
            <button type="button" class="btn btn-primary" id="confirm_range">Confirm</button>
          </div>
        </div>
      </div>
    </div>


<script>
  var update_every=parseInt('{{update_every}}');
  var max_no_updates=parseInt('{{max_no_updates}}');
  var no_update_count = 0;
  var total_steps = '{{total_steps}}';
  if(total_steps!=='None'){
      total_steps = parseInt(total_steps);
  }
  var current_step = 0;
  var data_str ='{{data|tojson}}';
  var nan_detect = false;
  var data = JSON.parse(data_str);
  window.chart_uuid = '{{chart_uuid}}';
  window.server_uuid = '{{server_uuid}}';
  window.log_dir = '{{log_dir}}';
  var finish = 'finish' in data;
  var range_charts = {}; // 当某个chart因为被选择到固定range时，就不要更新

  function create_chart(container, data){
      document.getElementById(container + '_title').style.visibility = 'visible';
      var chart = new G2.Chart({
        container: container,
        forceFit: true,
        height: document.body.clientHeight*0.40,
        padding: [20, 210, 30, 50],
      });
      // if(data.length>max_steps){
      //     data = data.slice(-max_steps);
      // }
      chart.source(data, {
        step: {
          range: [0, 1]
        }
      });
      chart.tooltip({
        useHtml:true,
        crosshairs: {
          type: 'line'
        },
        showTitle:false
      });
      // 使得两组颜色不一样
      if(container==='loss'){
          chart.line().position('step*value').color('name', ['#ff4d4f', '#ff7a45', '#ffa940', '#facc14', '#bae637',
              '#73d13d', '#36cfc9', '#40a9ff']).shape('smooth');
          chart.point().position('step*value').color('name', ['#ff4d4f', '#ff7a45', '#ffa940', '#facc14', '#bae637',
              '#73d13d', '#36cfc9', '#40a9ff']).size(5).shape('circle').style({
            stroke: '#fff',
            lineWidth: 1
          }).tooltip('name*epoch*step*value');
      }else{
          chart.line().position('step*value').color('name');
          chart.point().position('step*value').color('name').size(5).shape('circle').style({
            stroke: '#fff',
            lineWidth: 1
          }).tooltip('name**epoch*step*value');
      }

      chart.axis('step', {

      });

      chart.legend({
        useHtml: true,
        position: 'right-center',
        reactive: true,
        legendStyle: {
          MARKER_CLASS: {
            width: '20px',
            height: '18px',
            lineHeight: '18px',
            borderRadius: '50px',
            display: 'inline-block',
            marginRight: '4px',
            textAlign: 'center',
            fontZize: '10px',
            marginTop: '3px',
            color: 'white',
            float: 'left',
            borderStyle: 'solid',
            borderWidth: '1px',
            borderColor: '#ccc'
          }
        },
        containerTpl: '<div class="g2-legend" style="font-weight: bold;font-size: 20px">Choose what to show' +
            '<div class="g2-legend-list"></div></div>',
      });
      chart.render();
      return chart;
    }
  var charts = {};

  var loss_data;
  var metric_data;

  if('loss' in data){
      loss_data = data['loss'];
      if(is_nan_inf(loss_data)){
          nan_detect = true;
      }else{
        charts['loss'] = create_chart('loss', loss_data);
      }
      current_step = Math.max(current_step, loss_data[loss_data.length-1]['step']);
  }

  if('metric' in data){
      metric_data = data['metric'];
      if(is_nan_inf(metric_data)){
          nan_detect = true;
      }else{
        charts['metric'] = create_chart('metric', metric_data);
      }
      current_step = Math.max(current_step, metric_data[metric_data.length-1]['step']);
  }

  update_progress_bar(finish, current_step, total_steps);

  if(!finish && !nan_detect){
      update_tmp = get_new_steps();
  }else{
      success_prompt("All data have been loaded.", 2000);
  }

  function get_new_steps(){
    var tmp = setInterval(function(){
        $.ajax({
            url: '/chart/new_step',
            type: 'POST',
            dataType: 'json',
            contentType: 'application/json;charset=UTF-8',
            data: JSON.stringify({
                 chart_uuid: window.chart_uuid
            }),
            success: function(value){
                value = value['steps'];
                for(var key in value){
                    if(key==='finish'){
                        finish = true;
                        continue;
                    }
                    if(!(key in data)){
                        data[key] = [];
                    }
                    var _steps = value[key];

                    if(_steps.length>0){
                        if(is_nan_inf(_steps)){
                            stop_interval(tmp);
                            nan_detect = true;
                        }else{
                            for(var index=0;index<_steps.length;index++){
                                data[key].push(_steps[index]);
                                // if(data[key].length>max_steps){
                                //     data[key].shift();
                                // }
                            }
                            if(!(key in range_charts)){
                                if(!(key in charts)){
                                    charts[key] = create_chart(key, data[key]);
                                }else{
                                    var _chart = charts[key];
                                    _chart.changeData(data[key]);
                                }
                            }
                        }
                        current_step = Math.max(current_step, _steps[_steps.length-1]['step']);
                    }
                }
                if(finish || nan_detect) {
                    stop_interval(tmp);
                }
                update_progress_bar(finish, current_step, total_steps);
                if(finish){
                    bootbox.alert("Finish accessing data.");
                }
                if(jQuery.isEmptyObject(value)){
                    no_update_count += 1;
                }else{
                    no_update_count = 0;
                }
                if(no_update_count>max_no_updates){
                    stop_interval(tmp);
                }
            },
            error: function(error){
                bootbox.alert("Some error happens, stop updating data.");
                stop_interval(tmp);
            }
        });
    }, update_every);
    return tmp;
  }

    function stop_interval(tmp) {
        clearInterval(tmp)
    }

    function is_nan_inf(data) {
        // [{}, {}]
        var nan = false;
        for(var index=0;index<data.length;index++){
            var value = data[index];
            if(value['value']==='Infinity' || value['value']==='NaN' || value['value']==='-Infinity'){
                var msg = "";
                msg += value['value'] + " encountered in `" + value['name'] + "` at step:" + value['step'];
                if('epoch' in value){
                    msg += " at epoch:" + value['epoch'];
                }
                msg += '.';
                bootbox.alert(msg);
                nan = true;
                break;
            }
        }
        return nan;
    }

  function update_progress_bar(finish, current_step, total_steps){
      if($("#progress-bar-div").length>0){
            if(total_steps==='None' || current_step>total_steps){
                if(current_step>total_steps)
                    bootbox.alert("Step:"+ current_step +" larger than total steps:"+total_steps + ".");
                var bar_div = document.getElementById('progress-bar-div');
                bar_div.parentNode.removeChild(bar_div);
            }else{
                if(finish && (current_step===total_steps || current_step+1===total_steps)){
                    document.getElementById('progress-bar').className = 'progress-bar progress-bar-info';
                    document.getElementById('progress').className = 'progress';
                }else if(finish){
                    document.getElementById('progress-bar').className = 'progress-bar progress-bar-danger';
                    document.getElementById('progress').className = 'progress';
                }
                var percent = parseInt(current_step/total_steps*100);
                document.getElementById('progress-bar').style.width = percent + '%';
                document.getElementById('progress-bar-text').innerText = percent + '%';
                document.getElementById('progress-bar-div').style.visibility = 'visible';
            }
      }
  }

  var sliders = {};
  var $range = $("#range");
  var range_checked = {};
  var ranges = {};
  $range.click(function () {

      var tmp = {'loss':1, 'metric':1};
      for(var key in tmp){
          if(!(key in charts) && !(key in range_charts)){
              delete tmp[key];
          }
      }

      sliders = generate_range_modal(current_step, tmp, $("#range_modal"), range_checked, ranges);

      // 触发checkbox的点击
       $('input').change(function () {
          var $cb = $(this);
          if($cb[0].getAttribute('id')==='choose_range_checkbox') {
              var name = $cb[0].getAttribute('name');
              var state = $cb.prop('checked');
              if(state){
                  sliders[name].enable();
              }else{
                  sliders[name].disable();
              }
          }
       });
  });
  $('#range_box').on('hide.bs.modal', function (e) {
        $("#range_modal").empty();
  });

  // 确认选择了某个range
  $('#confirm_range').click(function () {
      var checkboxes = document.getElementsByClassName('toggle__input');
      var check_status = {};
      var checked_keys = [];
      var unchecked_keys = [];
      for(var index=0;index<checkboxes.length;index++){
          var checked = checkboxes[index].checked;
          var key = checkboxes[index].getAttribute('name');
          ranges[key] = sliders[key].getValue();
          check_status[key] = checked;
          if(checked){
              checked_keys.push(key);
          }else {
              unchecked_keys.push(key);
          }
      }
      //1.如果是checked
      if(checked_keys.length>0){
          $.ajax({
                url: '/chart/range',
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json;charset=UTF-8',
                data: JSON.stringify({
                    keys: checked_keys,
                    ranges: ranges,
                    log_dir: log_dir,
                    uuid: window.server_uuid
                }),
                success: function(value){
                    if(value['status']==='success'){
                        var steps = value['steps'];
                        for(var key in steps){
                            var _steps = steps[key];
                            //1. 从后端获取数据
                            if(_steps.length===0){
                                bootbox.alert("There is no data between step:" + ranges[key][0] +
                                    "-" + ranges[key][1] + " for `" + key +"`.");
                            }else{
                                //2. 如果返回的结果不为空; 没有nan值
                                if(!is_nan_inf(_steps)){
                                    //3. 删除已有的g2图
                                    //4. 替换为新的数据
                                    if(key in charts){
                                        charts[key].destroy();
                                        delete charts[key];
                                        document.getElementById(key + '_title').style.visibility = 'hidden';
                                    }
                                    if(key in range_charts){
                                        range_charts[key].changeData(_steps);
                                    }else{
                                        range_charts[key] = create_chart(key, _steps);
                                    }
                                }
                            }
                        }
                    }else{
                        bootbox.alert("Fail to get steps between step:" + ranges[key][0]  + "-" + ranges[key][1])
                    }
                },
                error: function(error){
                    bootbox.alert("Fail to get range data.")
                }
             })
      }
      //2.如果是unchecked
         //1. 根据range_checked判断是否之前是checked的
         //    1.1 是的话，恢复已有的g2图; 并从range_charts中删除
        //2. 之前也是uncheked的就不用管了
      if(unchecked_keys.length>0){
          for(var index=0;index<unchecked_keys.length;index++){
              var key = unchecked_keys[index];
              if(range_checked[key] === 'checked'){
                  range_charts[key].destroy();
                  document.getElementById(key + '_title').style.visibility = 'hidden';
                  if(key in data){
                      key_data = data[key];
                      if(is_nan_inf(key_data)){
                          nan_detect = true;
                      }else{
                        charts[key] = create_chart(key, key_data);
                      }
                      current_step = Math.max(current_step, key_data[key_data.length-1]['step']);
                  }
                  delete range_charts[key];
              }
          }
      }
      //3.修改range_checked为合适的值。
      if(checked_keys.indexOf('loss')!==-1){
          range_checked['loss'] = 'checked';
      }else{
          range_checked['loss'] = '';
      }
      if(checked_keys.indexOf('metric')!==-1){
          range_checked['metric'] = 'checked';
      }else{
          range_checked['metric'] = '';
      }

      $("#range_box").modal('hide');
      for(var key in sliders){
          sliders[key].destroy();
          delete sliders[key];
      }
  })

</script>
</body>
</html>
